#include "InternalFlash.h"

//读取指定地址的字（32位数据）
//faddr：读数据
//返回值：对应数据
u32 STMFLASH_ReadWord(u32 faddr)
{
	return *(__IO uint32_t *)faddr; 
}

//获取某个地址所在flash扇区
//addr:flash地址
//返回值：0~11，即addr所在扇区
uint16_t STMFLASH_GetFlashSector(u32 addr)
{
	if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
	else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
	else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
	else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;
	else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
	else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
	else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
	return FLASH_SECTOR_7;	
}

/**************************************************
功能：将数据字写入到内部FLAH里
参数：
			FlashAddress：准备写入的起始地址
			Data	：准备写入的数据字(宽度为32位)
			DataLength	：	需要写入的数据字的长度
返回：无
****************************************************/
//从指定地址开始写入指定长度的数据
//特别注意：因为STM32F7的扇区实在太大，没办法本地保存扇区数据，所以本函数
//写地址如果非0xff，那么会先擦除整个扇区且不保存扇区数据，所以写入非oxff的地址，将导致整个扇区数据丢失
//建议写入前确保扇区里没有重要数据，最好是整个扇区先擦除，然后慢慢往后写。
//该函数对OTP区域也有效!可以用来写OTP区
//OTP区域地址范围：0x1FF0F000~0X1FF0F41F
//writeAddr：起始地址*（此地址必须为4的整数倍）
//pBufferr：数据指针
//NumToWrite：字（32位）数就是要写入的32位数据的个数

u32 FLASH_If_Write(u32 WriteAddr, u32* pBuffer,u32 NumToWrite)
{
    FLASH_EraseInitTypeDef FlashEraseInit;
    HAL_StatusTypeDef FlashStatus=HAL_OK;
    u32 SectorError=0;
	u32 addrx=0;
	u32 endaddr=0;	
    if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)
	return 0;	//非法地址
    
 	HAL_FLASH_Unlock();             //解锁
	addrx=WriteAddr;				//写入的起始地址
	endaddr=WriteAddr+NumToWrite*4;	//写入的结束地址
    if(addrx<0X1FF00000)
    {
        while(addrx<endaddr)		//对非0XFF的地方先擦除
		{
			if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0xffffffff的地方，要擦除这个扇区
			{   
                FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型，扇区擦除
                FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区
                FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
                FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围VCC=2.7~3.6V之间
                if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) 
                {
                    break;//发生错误了
                }
                SCB_CleanInvalidateDCache();                            //清除无效的D-Cache
			}else addrx+=4;
            FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
        }
    }
    FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
	if(FlashStatus==HAL_OK)
	{
		while(WriteAddr<endaddr)//写数据
		{
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
			{ 
				break;	//写入异常
			}
			WriteAddr+=4;
			pBuffer++;
		} 
	}
	HAL_FLASH_Lock();           //上锁
	
}
/**************************************************
功能：从内部FLAH读取数据字到数组里
参数：
			FlashAddress：读取的起始地址
			Data	：读取的数据字保存的数组(宽度为32位)
			start_add	：从数组的第几个位置开始保存
			DataLength	：	需要读取的数据字的长度
返回：无

****************************************************/
//从指定地址开始读出指定长度的数据
//ReadAddr：起始地址
//pBuffer：数据指针
//NumToRead：字（32位）数
void FLASH_If_Read(u32 ReadAddr, u32* pBuffer, u32 start_add ,u32 NumToRead)
{
	u32 i ;
	pBuffer+=start_add;
	for (i = 0; i < NumToRead; i++)
	{
		pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节
		ReadAddr+=4;//偏移4个字节
	}
}
